/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package builder;

import composite.boolean_expression.Cond;
import composite.boolean_expression.Factb;
import composite.boolean_expression.Termb;
import composite.boolean_expression.factb.FactbCond;
import composite.boolean_expression.factb.FactbNot;
import composite.boolean_expression.factb.factb_expr.RelopEq;
import composite.boolean_expression.factb.factb_expr.RelopGe;
import composite.boolean_expression.factb.factb_expr.RelopGt;
import composite.boolean_expression.factb.factb_expr.RelopLe;
import composite.boolean_expression.factb.factb_expr.RelopLt;
import composite.boolean_expression.factb.factb_expr.RelopNeq;
import composite.number_expression.Expr;
import composite.number_expression.Fact;
import composite.number_expression.Term;
import composite.number_expression.Termp;
import composite.number_expression.fact.FactExpr;
import composite.number_expression.fact.FactId;
import composite.number_expression.fact.FactNum;
import composite.number_expression.term.AddTerm;
import composite.number_expression.term.MinusTerm;
import composite.number_expression.termp.DivTermp;
import composite.number_expression.termp.MultTermp;
import composite.number_expression.termp.RemTermp;
import exception.SyntaxException;
import java.io.Reader;
import lexer.AnalizzatoreLessicale;
import lexer.Simboli;

/**
 *
 * @author Antonio
 */
public class RecursiveBuilder implements BuilderIF {

    private AnalizzatoreLessicale lexer;
    private Cond root;
    private Simboli simbolo;

    public RecursiveBuilder(Reader in) {

        lexer = new AnalizzatoreLessicale(in);
        root = cond();
        atteso(Simboli.EOF);
    }

    @Override
    public Cond cond() {

        Cond cond = new Cond(termb());

        while (simbolo == Simboli.OR) {

            cond.add(termb());
        }

        return cond;
    }// cond

    @Override
    public Termb termb() {

        Termb termb = new Termb(factb());

        while (simbolo == Simboli.AND) {

            termb.add(factb());
        }

        return termb;

    }// termb

    @Override
    public Factb factb() {

        simbolo = lexer.prossimoSimbolo();

        if (simbolo == Simboli.NOT) {

            return new FactbNot(factb());
        } else if (simbolo == Simboli.TONDA_APERTA) {

            Cond cond = cond();
            atteso(Simboli.TONDA_CHIUSA);
            return new FactbCond(cond);
        } else {

            Expr left = expr();
            Expr right = null;

            if (simbolo == Simboli.EQ) {

                simbolo = lexer.prossimoSimbolo();
                right = expr();
                return new RelopEq(left, right);
            } else if (simbolo == Simboli.NEQ) {

                simbolo = lexer.prossimoSimbolo();
                right = expr();
                return new RelopNeq(left, right);
            } else if (simbolo == Simboli.GT) {

                simbolo = lexer.prossimoSimbolo();
                right = expr();
                return new RelopGt(left, right);
            } else if (simbolo == Simboli.GE) {

                simbolo = lexer.prossimoSimbolo();
                right = expr();
                return new RelopGe(left, right);
            } else if (simbolo == Simboli.LT) {

                simbolo = lexer.prossimoSimbolo();
                right = expr();
                return new RelopLt(left, right);
            } else if (simbolo == Simboli.LE) {

                simbolo = lexer.prossimoSimbolo();
                right = expr();
                return new RelopLe(left, right);
            } else {

                throw new SyntaxException("Atteso uno dei seguenti simboli: EQ, NEQ, LT, LE, GT, GE");
            }

        }
    }// factb

    @Override
    public Expr expr() {

        Expr expr = new Expr(term());

        while (simbolo == Simboli.MINUS || simbolo == Simboli.PLUS) {

            expr.add(term());
        }
        return expr;
    }// expr

    @Override
    public Term term() {

        Term term;

        if (simbolo == Simboli.PLUS) {

            simbolo = lexer.prossimoSimbolo();
            term = new AddTerm(termp());
        } else if (simbolo == Simboli.MINUS) {

            simbolo = lexer.prossimoSimbolo();
            term = new MinusTerm(termp());
        } else {

            term = new Term(termp());
        }

        while (simbolo == Simboli.MULT || simbolo == Simboli.DIV || simbolo == Simboli.REM) {

            term.add(termp());
        }
        return term;
    }// term

    @Override
    public Termp termp() {

        Termp termp;

        if (simbolo == Simboli.MULT) {

            simbolo = lexer.prossimoSimbolo();
            termp = new MultTermp(fact());
        } else if (simbolo == Simboli.DIV) {

            simbolo = lexer.prossimoSimbolo();
            termp = new DivTermp(fact());
        } else if (simbolo == Simboli.REM) {

            simbolo = lexer.prossimoSimbolo();
            termp = new RemTermp(fact());
        } else {

            termp = new Termp(fact());
        }

        while (simbolo == Simboli.POWER) {

            simbolo = lexer.prossimoSimbolo();
            termp.add(fact());
        }
        return termp;
    }// termp

    @Override
    public Fact fact() {

        Fact res;

        if (simbolo == Simboli.ID) {

            res = new FactId(lexer.getString());
            simbolo = lexer.prossimoSimbolo();
        } else if (simbolo == Simboli.NUM) {

            res = new FactNum(lexer.getString());
            simbolo = lexer.prossimoSimbolo();
        } else if (simbolo == Simboli.QUADRA_APERTA) {

            simbolo = lexer.prossimoSimbolo();
            Expr expr = expr();
            atteso(Simboli.QUADRA_CHIUSA);
            res = new FactExpr(expr);

        } else {

            throw new SyntaxException(
                    "Atteso NUM, ID o EXPR");
        }

        return res;
    }// fact

    private void atteso(Simboli s) {

        if (simbolo != s) {

            String msg = " trovato " + simbolo + " mentre si attendeva " + s;
            throw new SyntaxException(msg);
        }
        simbolo = lexer.prossimoSimbolo();
    }// atteso

    public Cond getCond() {

        return root;
    }// getCond
}
